package cafe.adriel.androidaudiorecorder.slice;

import cafe.adriel.androidaudiorecorder.ResourceTable;
import com.example.likestarlibrary.TCHeartLayout;
import com.example.omrecorder.AudioChunk;
import com.example.omrecorder.OmRecorder;
import com.example.omrecorder.PullTransport;
import com.example.omrecorder.Recorder;
import cafe.adriel.androidaudiorecorder.OhosAudioRecorder;
import cafe.adriel.androidaudiorecorder.Util;
import cafe.adriel.androidaudiorecorder.model.AudioChannel;
import cafe.adriel.androidaudiorecorder.model.AudioSampleRate;
import cafe.adriel.androidaudiorecorder.model.AudioSource;

import com.scwang.wave.MultiWaveHeader;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.common.Source;
import ohos.media.player.Player;
import ohos.utils.IntervalTimer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import static ohos.agp.components.Component.INVISIBLE;
import static ohos.agp.components.Component.VISIBLE;

/**
 * ex.
 * AudioRecorderAbilitySlice
 *
 * @author lj
 * @since 2021-05-12
 * @param <MediaPlayer>
 */
public class AudioRecorderAbilitySlice<MediaPlayer> extends AbilitySlice
        implements Component.ClickedListener, PullTransport.OnAudioChunkPulledListener {
    private static final int RED_COLOR = 0xff0000;
    private static final int GREEN_COLOR = 0x00ff00;
    private static final int BLUE_COLOR = 0x0000ff;
    private static final int HEXADECIMAL = 16;
    private static final int OCTAL = 8;
    private String filePath;
    private AudioSource source;
    private AudioSampleRate sampleRate;
    private int color;
    private boolean autoStart;
    private boolean keepDisplayOn;
    /**
     *  Text btn_datetime
     */
    private Text btn_datetime;
    private Text mTextDescription;
    private Image image_clear;
    private Image image_check;
    private Image btn_cancle;
    private Image btn_recorder;
    private Image btn_play;
    private Recorder mRecorder;
    private Player mPlayer;
    private EventHandler mHander;
    private File file = null;
    private Timer timer;
    private Timer voicetimer = new Timer();
    private int recorderSecondsElapsed;
    private boolean isRecording;
    private int playerSecondsElapsed;
    private DependentLayout contentLayout;
    private MultiWaveHeader mWaveHeader;
    private int[] drawableIds = new int[]{
        ResourceTable.Id_heart_layout, ResourceTable.Id_heart_layout1,
        ResourceTable.Id_heart_layout2, ResourceTable.Id_heart_layout3,
        ResourceTable.Id_heart_layout4, ResourceTable.Id_heart_layout5,
        ResourceTable.Id_heart_layout6, ResourceTable.Id_heart_layout7,
        ResourceTable.Id_heart_layout8
    };
    private IntervalTimer intervalTimer;
    private AudioChannel channel;
    private AnimatorValue animatorValue = new AnimatorValue();

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_audio_recorder);
        if (intent != null) {
            filePath = intent.getStringParam(OhosAudioRecorder.EXTRA_FILE_PATH);
            source = intent.getSerializableParam(OhosAudioRecorder.EXTRA_SOURCE);
            channel = intent.getSerializableParam(OhosAudioRecorder.EXTRA_CHANNEL);
            sampleRate = intent.getSerializableParam(OhosAudioRecorder.EXTRA_SAMPLE_RATE);
            color = intent.getIntParam(OhosAudioRecorder.EXTRA_COLOR, 0);
            autoStart = intent.getBooleanParam(OhosAudioRecorder.EXTRA_AUTO_START, false);
            keepDisplayOn = intent.getBooleanParam(OhosAudioRecorder.EXTRA_KEEP_DISPLAY_ON, false);
        } else {
            filePath = getAbility().getIntent().getStringParam(OhosAudioRecorder.EXTRA_FILE_PATH);
            source = getAbility().getIntent().getSerializableParam(OhosAudioRecorder.EXTRA_SOURCE);
            sampleRate = getAbility().getIntent().getSerializableParam(OhosAudioRecorder.EXTRA_SAMPLE_RATE);
            color = getAbility().getIntent().getIntParam(OhosAudioRecorder.EXTRA_COLOR, 0);
            autoStart = getAbility().getIntent().getBooleanParam(OhosAudioRecorder.EXTRA_AUTO_START, false);
            keepDisplayOn = getAbility().getIntent().getBooleanParam(OhosAudioRecorder.EXTRA_KEEP_DISPLAY_ON, false);
        }
        initRecorder();
        initCommpont();
        initWaveCommpont();
        initView();
    }

    /**
     *
     * 初始化initView
     */
    private void initView() {
        intervalTimer = new IntervalTimer(600000, 300) {
            @Override
            public void onInterval(long remain) {
                // This method will be called at the 3000th millisecond and repeatedly called every 3000 milliseconds.
                TaskDispatcher globalTaskDispatcher = getUITaskDispatcher();
                globalTaskDispatcher.asyncDispatch(new Runnable() {
                    @Override
                    public void run() {
                        final double val = Math.random();
                        final int sval = (int)(val*7);
                        TCHeartLayout componentById = (TCHeartLayout) findComponentById(drawableIds[sval]);
                        componentById.setImgColor(color);
                        componentById.addFavor();
                    }
                });
            }

            @Override
            public void onFinish() {
                // This method will be called at the 60000th millisecond.
            }
        };
    }

    private void initCommpont() {
        contentLayout = (DependentLayout) findComponentById(ResourceTable.Id_content);
        btn_datetime = (Text) findComponentById(ResourceTable.Id_text_date);
        mTextDescription = (Text) findComponentById(ResourceTable.Id_text_description);
        btn_cancle = (Image) findComponentById(ResourceTable.Id_cancle);
        btn_cancle.setTag(0);
        btn_recorder = (Image) findComponentById(ResourceTable.Id_recorder);
        btn_recorder.setTag(1);
        btn_play = (Image) findComponentById(ResourceTable.Id_play);
        btn_play.setTag(2);
        image_clear = (Image) findComponentById(ResourceTable.Id_image_clear);
        image_clear.setTag(3);
        image_check = (Image) findComponentById(ResourceTable.Id_image_check);
        image_check.setTag(4);
        btn_cancle.setClickedListener(this::onClick);
        btn_recorder.setClickedListener(this::onClick);
        btn_play.setClickedListener(this::onClick);
        image_clear.setClickedListener(this::onClick);
        image_check.setClickedListener(this::onClick);
        mHander = new EventHandler(EventRunner.getMainEventRunner());
        mPlayer = new Player(this);
        ShapeElement shapeElement = new ShapeElement();
        int buttonRed = (Util.getDarkerColor(color) & RED_COLOR) >> HEXADECIMAL;
        int buttonGreen = (Util.getDarkerColor(color) & GREEN_COLOR) >> OCTAL;
        int buttonBlue = Util.getDarkerColor(color) & BLUE_COLOR;
        shapeElement.setRgbColor(new RgbColor(buttonRed, buttonGreen, buttonBlue));
        contentLayout.setBackground(shapeElement);
    }

    private void initWaveCommpont() {
        mWaveHeader = (MultiWaveHeader) findComponentById(ResourceTable.Id_multiWaveHeader);
        mWaveHeader.setRotation(180f);
        mWaveHeader.setStartColor(color);
        mWaveHeader.setWaves("0,0,1,1,15\n");
        mWaveHeader.setAmplitude(0);
    }

    @Override
    public void onActive() {
        onAbilityResult(OhosAudioRecorder.REQUESTCODE, OhosAudioRecorder.RESULT_CANCELED, null);
        super.onActive();
    }

    private void startPlay() {

        if (file.exists()) {
            mPlayer.reset();
            FileInputStream inputStream = null;
            try {
                inputStream = new FileInputStream(file.getCanonicalFile());
                mPlayer.setSource(new Source(inputStream.getFD()));
                mPlayer.prepare();
                mPlayer.setPlayerCallback(new Player.IPlayerCallback() {
                    @Override
                    public void onPrepared() {
                    }

                    @Override
                    public void onMessage(int i, int i1) {
                    }

                    @Override
                    public void onError(int i, int i1) {
                    }

                    @Override
                    public void onResolutionChanged(int i, int i1) {
                    }

                    @Override
                    public void onPlayBackComplete() {
                        intervalTimer.cancel();
                        stopTimer();
                        stopVoiceTimer();
                        mHander.postTask(() -> mTextDescription.setVisibility(Component.INVISIBLE));
                        mHander.postTask(() -> btn_play.setPixelMap(ResourceTable.Media_aar_ic_play));
                    }

                    @Override
                    public void onRewindToComplete() {
                        intervalTimer.cancel();
                        stopTimer();
                        stopVoiceTimer();
                        mHander.postTask(() -> mTextDescription.setVisibility(Component.INVISIBLE));
                        mHander.postTask(() -> btn_play.setPixelMap(ResourceTable.Media_aar_ic_play));
                    }

                    @Override
                    public void onBufferingChange(int i) {
                    }

                    @Override
                    public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
                    }

                    @Override
                    public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
                    }
                });
                mPlayer.play();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private void initRecorder() {
        file = new File(getFilesDir() + "/recorded_audio.aac");
        try {
            boolean isCreate = file.createNewFile();
            if (!isCreate) {
                throw new IOException("Unable to create file at specified path. It already exists");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createTimer() {
        stopTimer();
        timer = new Timer();
        /**
         *定时器
         * TimerTask
         *  参数1 执行方法
         *  参数2 延迟时间 单位毫秒
         *  参数3 间隔时间 毫秒单位
         */
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                updateTimer();
            }
        }, 0, 1000);
    }

    private void updateTimer() {
        /**
         * 一部分发
         */
        getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                if (isRecording) {
                    recorderSecondsElapsed++;
                    btn_datetime.setText(Util.formatSeconds(recorderSecondsElapsed));
                } else if (isPlaying()) {
                    playerSecondsElapsed++;
                    btn_datetime.setText(Util.formatSeconds(playerSecondsElapsed));
                }
            }
        });
    }

    private void stopVoiceTimer() {
        if (voicetimer != null) {
            voicetimer.cancel();
            voicetimer.purge();
            voicetimer = null;
        }
    }

    private void stopTimer() {
        if (timer != null) {
            timer.cancel();
            timer.purge();
            timer = null;
        }
    }

    private void getVoiceValueTimer(float amplitude) {

        amplitude = Math.max(Math.min(50, amplitude / 2), 10);

        mWaveHeader.setAmplitude((int) amplitude);

    }

    @Override
    public void onClick(Component component) {
        int tag = Integer.parseInt(component.getTag().toString());
        switch (tag) {
            case 0:
                restartRecording();
                break;
            case 1:
                stopPlaying();
                Util.wait(100, new Runnable() {
                    @Override
                    public void run() {
                        if (isRecording) {
                            pauseRecording();
                        } else {

                            mWaveHeader.startWave();
                            resumeRecording();
                        }
                    }
                }, getContext());
                break;
            case 2:
                pauseRecording();
                Util.wait(100, new Runnable() {
                    @Override
                    public void run() {
                        if (isPlaying()) {
                            stopPlaying();
                        } else {
                            startPlaying();
                        }
                    }
                }, getContext());

                break;
            case 3:
                stopRecording();
                getAbility().setResult(OhosAudioRecorder.RESULT_CANCELED, null);
                terminateAbility();
                break;
            case 4:
                selectAudio();
                break;
            default:
        }
    }

    private void resumeRecording() {
        isRecording = true;
        mTextDescription.setText("Recording");
        mTextDescription.setVisibility(VISIBLE);
        mWaveHeader.setVelocity(1f * 70 / 10);
        intervalTimer.schedule();
        btn_recorder.setPixelMap(ResourceTable.Media_aar_ic_pause);
        btn_play.setPixelMap(ResourceTable.Media_aar_ic_play);
        btn_cancle.setVisibility(INVISIBLE);
        btn_play.setVisibility(INVISIBLE);
        image_check.setVisibility(INVISIBLE);
        mPlayer.stop();
        if (mRecorder == null) {
            btn_datetime.setText("00:00:00");
            mRecorder = OmRecorder.wav(
                    new PullTransport.Default(Util.getMic(source, channel, sampleRate), AudioRecorderAbilitySlice.this),
                    file);
        }
        mRecorder.resumeRecording();
        createTimer();
    }

    private void pauseRecording() {
        isRecording = false;
        intervalTimer.cancel();
        mTextDescription.setText("Paused");
        mTextDescription.setVisibility(VISIBLE);
        btn_cancle.setVisibility(VISIBLE);
        btn_play.setVisibility(VISIBLE);
        image_check.setVisibility(VISIBLE);
        btn_recorder.setPixelMap(ResourceTable.Media_aar_ic_rec);
        btn_play.setPixelMap(ResourceTable.Media_aar_ic_play);
        mWaveHeader.release();
        mWaveHeader.stop();
        if (mRecorder != null) {
            mRecorder.pauseRecording();
        }
        stopTimer();
        stopVoiceTimer();
    }

    /**
     * 获取资源
     *
     * startPlaying
     */
    private void startPlaying() {
        try {
            stopRecording();
            intervalTimer.schedule();
            mTextDescription.setText("Playing");
            mTextDescription.setVisibility(VISIBLE);
            mPlayer = new Player(this);
            startPlay();
            btn_play.setPixelMap(ResourceTable.Media_aar_ic_stop);
            btn_datetime.setText("00:00:00");
            playerSecondsElapsed = 0;
            createTimer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void stopRecording() {
        mWaveHeader.release();
        mWaveHeader.stop();
        if (mRecorder != null) {
            mRecorder.stopRecording();
            mRecorder = null;
        }
        intervalTimer.cancel();
        recorderSecondsElapsed = 0;
        stopTimer();
        stopVoiceTimer();
    }

    private void selectAudio() {
        stopRecording();
        AudioRecorderAbilitySlice.this.getAbility().setResult(OhosAudioRecorder.RESULT_OK, null);
        terminateAbility();
    }

    @Override
    protected void onForeground(Intent intent) {
        mWaveHeader.start();
        super.onForeground(intent);
    }

    @Override
    protected void onBackground() {
        restartRecording();
        super.onBackground();
    }
    @Override
    public void onInactive() {
        super.onActive();
    }
    @Override
    protected void onStop() {
        restartRecording();
        super.onStop();
    }

    public void restartRecording() {
        if (isRecording) {
            stopRecording();
        } else if (isPlaying()) {
            stopPlaying();
        } else {
            mWaveHeader.release();
            mWaveHeader.stop();
        }
        if (animatorValue != null) {
            animatorValue.stop();
        }

        intervalTimer.cancel();
        mPlayer.stop();
        isRecording = false;
        stopTimer();
        stopVoiceTimer();
        btn_play.setPixelMap(ResourceTable.Media_aar_ic_play);
        btn_cancle.setVisibility(INVISIBLE);
        btn_play.setVisibility(INVISIBLE);
        image_check.setVisibility(INVISIBLE);
        mTextDescription.setVisibility(INVISIBLE);
        btn_recorder.setPixelMap(ResourceTable.Media_aar_ic_rec);
        btn_datetime.setText("00:00:00");
        recorderSecondsElapsed = 0;
        playerSecondsElapsed = 0;
    }

    private boolean isPlaying() {
        try {
            return mPlayer != null && mPlayer.isNowPlaying() && !isRecording;
        } catch (Exception e) {
            return false;
        }
    }

    private void stopPlaying() {
        mTextDescription.setText(" ");
        mTextDescription.setVisibility(INVISIBLE);
        mWaveHeader.release();
        mWaveHeader.stop();

        if (mPlayer != null) {
            try {
                mPlayer.stop();
                mPlayer.reset();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        intervalTimer.cancel();
        stopVoiceTimer();
        btn_play.setPixelMap(ResourceTable.Media_aar_ic_play);
        stopTimer();
    }

    @Override
    public void onAudioChunkPulled(AudioChunk audioChunk) {
        float amplitude = isRecording ? (float) audioChunk.maxAmplitude() : 0f;
        System.out.println("amplitude :" + amplitude);
        getVoiceValueTimer(amplitude);
    }
}
